This file is used to identify specific markers for IBL and ORS.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

out_dir = "."

We load the dataset :

sobj = readRDS(paste0(out_dir, "/hs_hd_sobj.rds"))
sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

We load the sample information :

sample_info = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_sample_info.rds"))
project_names_oi = sample_info$project_name

graphics::pie(rep(1, nrow(sample_info)),
              col = sample_info$color,
              labels = sample_info$project_name)

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

This is the projection of interest :

name2D = "harmony_38_tsne"

We design a custom function to make a histogram and a wordcloud to visualize differentially expressed genes :

hist_wc_fun = function(mark, col) {
  cut_colors = c("firebrick4", "firebrick2", "indianred1", "darksalmon",
                 "lightpink", "gray50", "khaki3", "darkolivegreen1",
                 "olivedrab1", "chartreuse2", "chartreuse4")
  cut_colors_cont = c(rev(RColorBrewer::brewer.pal(name = "Reds", n = 9)[c(5:9)]),
                      RColorBrewer::brewer.pal(name = "Greens", n = 9)[c(5:9)])
  
  if (col == "pct.1") {
    mark$pct = mark$pct.1
    mark$pct_cut = mark$pct.1_cut
  } else if (col == "pct.2") {
    mark$pct = mark$pct.2
    mark$pct_cut = mark$pct.2_cut
    cut_colors = rev(cut_colors)
    cut_colors_cont = rev(cut_colors_cont)
  } else {
    stop("col must be either pct.1 or pct.2")
  }
  
  p_hist = ggplot2::ggplot(mark, mapping = aes(x = avg_logFC, fill = pct_cut)) +
    ggplot2::geom_histogram(binwidth = 0.05) +
    ggplot2::scale_fill_manual(breaks = levels(mark$pct_cut),
                               values = cut_colors,
                               name = paste0(col, "_cut")) +
    ggplot2::theme_classic()
  
  p_wc = ggplot2::ggplot(mark, aes(label = gene_name, size = avg_logFC, color = pct)) +
    ggwordcloud::geom_text_wordcloud_area(show.legend = TRUE, seed = 1) +
    ggplot2::scale_color_gradientn(colors = cut_colors_cont,
                                   name = col) +
    ggplot2::scale_size_area(max_size = 5) +
    ggplot2::theme_minimal() +
    ggplot2::guides(size = "none")
  
  p = patchwork::wrap_plots(p_hist, p_wc, nrow = 1)
  
  return(p)
}

Visualization

Gene expression

We visualize gene expression for some markers :

features = c("percent.mt", "percent.rb", "nFeature_RNA")

plot_list = lapply(features, FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster type Clusters and cell type

We visualize clusters and cell type :

cluster_plot = Seurat::DimPlot(sobj, group.by = "seurat_clusters",
                               reduction = name2D, label = TRUE) +
  ggplot2::labs(title = "Cluster ID") +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

cell_type_plot = Seurat::DimPlot(sobj, group.by = "cell_type",
                                 reduction = name2D, label = FALSE) +
  ggplot2::scale_color_manual(values = color_markers,
                              breaks = names(color_markers)) +
  ggplot2::labs(title = "Cell type") +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

cell_type_plot | cluster_plot

We summarize major cell type by cluster :

cell_type_clusters = sobj@meta.data[, c("cell_type", "seurat_clusters")] %>%
  table() %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cell_type_clusters = setNames(levels(sobj$cell_type)[cell_type_clusters],
                              nm = names(cell_type_clusters))

We define cluster type :

sobj$cluster_type = cell_type_clusters[sobj$seurat_clusters] %>%
  as.factor()
table(sobj$cluster_type, sobj$cell_type)
##                   
##                    CD4 T cells CD8 T cells Langerhans cells macrophages B cells
##   B cells                    0           0                0           0      37
##   CD4 T cells              774          52                2           1       3
##   CD8 T cells               86         545                1           0       4
##   HFSC                       0           0                0           0       2
##   IBL                        0           0                0           0       1
##   IFE                        0           0                0           0       1
##   IRS                        0           0                0           0       0
##   Langerhans cells          15           0              253          51       1
##   ORS                        2           0                0           0       3
##   cortex                     0           0                0           0       0
##   cuticle                    1           0                0           0       0
##   macrophages                9           0               26         423       1
##   medulla                    0           0                0           0       1
##   proliferative              0           0                3           0      12
##   sebocytes                  0           0                0           0       0
##                   
##                    cuticle cortex medulla  IRS proliferative  IBL  ORS  IFE
##   B cells                0      0       1    0             0    0    0    0
##   CD4 T cells            2      0       3    3             2    1    0    0
##   CD8 T cells            0      0       0    0             0    0    0    0
##   HFSC                   4      0       2    2             9   41  104    3
##   IBL                    4      0       5    1            23 1502   11  113
##   IFE                    1      0       0    0             2   25   66  498
##   IRS                    0      0       0  155             4    0    0    0
##   Langerhans cells       1      1       0    2            23    2    0    0
##   ORS                    5      2      15    6            23   21 1779   57
##   cortex               105    199       4    0             0    0    0    0
##   cuticle              804     29      21    0             1    0    0    0
##   macrophages            0      0       0    1             0    0    0    0
##   medulla               26      1     335    0            17    0    0    0
##   proliferative        318      8      42  126          1384   44   75   71
##   sebocytes              0      1       1    0             1    0   63    1
##                   
##                    HFSC sebocytes
##   B cells             0         0
##   CD4 T cells         0         0
##   CD8 T cells         0         0
##   HFSC             1286         1
##   IBL                23         7
##   IFE                 7        13
##   IRS                 0         0
##   Langerhans cells    1         2
##   ORS                44        16
##   cortex              0         0
##   cuticle             0         2
##   macrophages         0         0
##   medulla             1         0
##   proliferative      24        18
##   sebocytes           0       154

We compare cluster annotation and cell type annotation :

cell_type_plot

p2 = Seurat::DimPlot(sobj, group.by = "cluster_type",
                     reduction = name2D, cols = color_markers) +
  ggplot2::labs(title = "Cluster type") +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

patchwork::wrap_plots(cell_type_plot, p2, guides = "collect")

There is mis-annotation in ORS (small part close to IBL) and in IBL (small part in IFE), so we keep the single-cell level cell type annotation.

Differential expression

In this section, we perform DE between inner bulge layer (IBL) or outer root sheath (ORS), and all remaining cells. We save the results in a list :

list_results = list()

We change cell identities to cell type :

Seurat::Idents(sobj) = sobj$cell_type

table(Seurat::Idents(sobj))
## 
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##              887              597              285              475 
##          B cells          cuticle           cortex          medulla 
##               66             1270              241              429 
##              IRS    proliferative              IBL              ORS 
##              296             1489             1636             2098 
##              IFE             HFSC        sebocytes 
##              743             1386              213

IBL markers

In this section, we compared IBL to other cells.

group_name = "IBL_vs_all"

aquarius::plot_red_and_blue(sobj,
                            group1 = "IBL",
                            reduction = name2D) +
  ggplot2::labs(title = group_name)

Differential expression

We identify specific markers for each population :

mark = Seurat::FindMarkers(sobj, ident.1 = "IBL")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)
mark$gene_name = rownames(mark)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 970   6
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2     p_val_adj gene_name
## KRT16     0.000000e+00  3.892906 0.905 0.155  0.000000e+00     KRT16
## KRT6B     0.000000e+00  3.166639 0.921 0.275  0.000000e+00     KRT6B
## KRT17     0.000000e+00  2.954873 0.980 0.529  0.000000e+00     KRT17
## KRT6A     0.000000e+00  2.860010 0.752 0.260  0.000000e+00     KRT6A
## S100A2    0.000000e+00  2.635463 0.992 0.789  0.000000e+00    S100A2
## KRT6C     0.000000e+00  2.494914 0.548 0.068  0.000000e+00     KRT6C
## KRT5      0.000000e+00  1.988478 0.993 0.749  0.000000e+00      KRT5
## FABP5     0.000000e+00  1.975885 0.989 0.668  0.000000e+00     FABP5
## GJB6      0.000000e+00  1.912305 0.908 0.525  0.000000e+00      GJB6
## KRT14     0.000000e+00  1.758304 0.992 0.699  0.000000e+00     KRT14
## NDUFA4L2  0.000000e+00  1.721714 0.680 0.212  0.000000e+00  NDUFA4L2
## CST6      0.000000e+00  1.666433 0.384 0.076  0.000000e+00      CST6
## GJB2      0.000000e+00  1.640163 0.826 0.503  0.000000e+00      GJB2
## HSPB1     0.000000e+00  1.630779 0.985 0.835  0.000000e+00     HSPB1
## TM4SF1    0.000000e+00  1.597284 0.801 0.243  0.000000e+00    TM4SF1
## CALML3    0.000000e+00  1.590650 0.958 0.685  0.000000e+00    CALML3
## SDC1      0.000000e+00  1.582781 0.829 0.544  0.000000e+00      SDC1
## S100A16   0.000000e+00  1.547152 0.955 0.720  0.000000e+00   S100A16
## ANXA2     0.000000e+00  1.518822 0.991 0.852  0.000000e+00     ANXA2
## LGALS7B  3.086968e-238  1.508193 0.748 0.465 6.174861e-234   LGALS7B

How many genes enriched in IBL ?

mark_IBL = mark %>%
  dplyr::filter(avg_logFC > 0)

nrow(mark_IBL)
## [1] 336

We cut pct.1 and pct.2 by bins of 0.1 :

mark_IBL$pct.1_cut = cut(mark_IBL$pct.1, breaks = 10)
mark_IBL$pct.2_cut = cut(mark_IBL$pct.2, breaks = 10)

head(mark_IBL)
##        p_val avg_logFC pct.1 pct.2 p_val_adj gene_name     pct.1_cut
## KRT16      0  3.892906 0.905 0.155         0     KRT16 (0.905,0.995]
## KRT6B      0  3.166639 0.921 0.275         0     KRT6B (0.905,0.995]
## KRT17      0  2.954873 0.980 0.529         0     KRT17 (0.905,0.995]
## KRT6A      0  2.860010 0.752 0.260         0     KRT6A (0.726,0.815]
## S100A2     0  2.635463 0.992 0.789         0    S100A2 (0.905,0.995]
## KRT6C      0  2.494914 0.548 0.068         0     KRT6C (0.547,0.637]
##              pct.2_cut
## KRT16      (0.102,0.2]
## KRT6B      (0.2,0.297]
## KRT17    (0.493,0.591]
## KRT6A      (0.2,0.297]
## S100A2   (0.786,0.884]
## KRT6C  (0.00302,0.102]

Visualization

We make a histogram for pct.1, pct.2 and avg_logFC.

hist_wc_fun(mark_IBL, "pct.1")

hist_wc_fun(mark_IBL, "pct.2")

Selection

The best markers have high pct.1 and low pct.2 :

mark_IBL = mark_IBL %>%
  dplyr::filter(pct.1 > 0.6 & pct.2 < 0.3)

list_results[[group_name]]$choosen_ones = mark_IBL

mark_IBL
##          p_val avg_logFC pct.1 pct.2 p_val_adj gene_name     pct.1_cut
## KRT16        0  3.892906 0.905 0.155         0     KRT16 (0.905,0.995]
## KRT6B        0  3.166639 0.921 0.275         0     KRT6B (0.905,0.995]
## KRT6A        0  2.860010 0.752 0.260         0     KRT6A (0.726,0.815]
## NDUFA4L2     0  1.721714 0.680 0.212         0  NDUFA4L2 (0.637,0.726]
## TM4SF1       0  1.597284 0.801 0.243         0    TM4SF1 (0.726,0.815]
## LYPD3        0  1.408274 0.732 0.289         0     LYPD3 (0.726,0.815]
##            pct.2_cut
## KRT16    (0.102,0.2]
## KRT6B    (0.2,0.297]
## KRT6A    (0.2,0.297]
## NDUFA4L2 (0.2,0.297]
## TM4SF1   (0.2,0.297]
## LYPD3    (0.2,0.297]

We visualize expression levels of those genes on the projection :

plot_list = lapply(rownames(mark_IBL), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

ORS markers

In this section, we compared ORS to other cells.

group_name = "ORS_vs_all"

aquarius::plot_red_and_blue(sobj,
                            group1 = "ORS",
                            reduction = name2D) +
  ggplot2::labs(title = group_name)

Differential expression

We identify specific markers for each population :

mark = Seurat::FindMarkers(sobj, ident.1 = "ORS")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)
mark$gene_name = rownames(mark)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 739   6
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2     p_val_adj gene_name
## KRT15    0.000000e+00 1.2525184 0.929 0.367  0.000000e+00     KRT15
## ATP1B3   0.000000e+00 1.2365428 0.973 0.628  0.000000e+00    ATP1B3
## CCL2     0.000000e+00 1.1973508 0.506 0.056  0.000000e+00      CCL2
## DST      0.000000e+00 1.1908763 0.981 0.352  0.000000e+00       DST
## IMPA2    0.000000e+00 1.0522498 0.924 0.455  0.000000e+00     IMPA2
## COL17A1  0.000000e+00 0.9568857 0.923 0.299  0.000000e+00   COL17A1
## TXNIP    0.000000e+00 0.9062837 0.955 0.547  0.000000e+00     TXNIP
## S100A9   0.000000e+00 0.9041040 0.691 0.231  0.000000e+00    S100A9
## ZFP36L2  0.000000e+00 0.8729918 0.968 0.655  0.000000e+00   ZFP36L2
## IFITM3   0.000000e+00 0.8616994 0.909 0.433  0.000000e+00    IFITM3
## NEAT1    0.000000e+00 0.8281953 0.956 0.672  0.000000e+00     NEAT1
## MOXD1    0.000000e+00 0.8157750 0.764 0.126  0.000000e+00     MOXD1
## CEBPD    0.000000e+00 0.8026200 0.886 0.439  0.000000e+00     CEBPD
## AQP3     0.000000e+00 0.7900901 0.796 0.263  0.000000e+00      AQP3
## PTN     5.159311e-271 0.7884902 0.567 0.234 1.032017e-266       PTN
## ALDH3A1  0.000000e+00 0.7620031 0.607 0.087  0.000000e+00   ALDH3A1
## ARL4A    0.000000e+00 0.7563103 0.874 0.504  0.000000e+00     ARL4A
## GPX2     0.000000e+00 0.7468617 0.633 0.112  0.000000e+00      GPX2
## LIMA1    0.000000e+00 0.7441636 0.893 0.379  0.000000e+00     LIMA1
## FST      0.000000e+00 0.7440017 0.513 0.064  0.000000e+00       FST

How many genes enriched in ORS ?

mark_ORS = mark %>%
  dplyr::filter(avg_logFC > 0)

nrow(mark_ORS)
## [1] 275

We cut pct.1 and pct.2 by bins of 0.1 :

mark_ORS$pct.1_cut = cut(mark_ORS$pct.1, breaks = 10)
mark_ORS$pct.2_cut = cut(mark_ORS$pct.2, breaks = 10)

head(mark_ORS)
##         p_val avg_logFC pct.1 pct.2 p_val_adj gene_name     pct.1_cut
## KRT15       0 1.2525184 0.929 0.367         0     KRT15 (0.911,0.999]
## ATP1B3      0 1.2365428 0.973 0.628         0    ATP1B3 (0.911,0.999]
## CCL2        0 1.1973508 0.506 0.056         0      CCL2 (0.479,0.566]
## DST         0 1.1908763 0.981 0.352         0       DST (0.911,0.999]
## IMPA2       0 1.0522498 0.924 0.455         0     IMPA2 (0.911,0.999]
## COL17A1     0 0.9568857 0.923 0.299         0   COL17A1 (0.911,0.999]
##             pct.2_cut
## KRT15   (0.309,0.407]
## ATP1B3  (0.603,0.701]
## CCL2    (0.013,0.112]
## DST     (0.309,0.407]
## IMPA2   (0.407,0.505]
## COL17A1  (0.21,0.309]

Visualization

We make a histogram for pct.1, pct.2 and avg_logFC.

hist_wc_fun(mark_ORS, "pct.1")

hist_wc_fun(mark_ORS, "pct.2")

Selection

The best markers have high pct.1 and low pct.2 :

mark_ORS = mark_ORS %>%
  dplyr::filter(pct.1 > 0.6 & pct.2 < 0.2)

list_results[[group_name]]$choosen_ones = mark_ORS

mark_ORS
##         p_val avg_logFC pct.1 pct.2 p_val_adj gene_name     pct.1_cut
## MOXD1       0 0.8157750 0.764 0.126         0     MOXD1 (0.738,0.825]
## ALDH3A1     0 0.7620031 0.607 0.087         0   ALDH3A1 (0.566,0.652]
## GPX2        0 0.7468617 0.633 0.112         0      GPX2 (0.566,0.652]
## AHNAK2      0 0.7113339 0.749 0.184         0    AHNAK2 (0.738,0.825]
## CDH13       0 0.6071029 0.741 0.158         0     CDH13 (0.738,0.825]
## LAMB3       0 0.6026591 0.740 0.163         0     LAMB3 (0.738,0.825]
## S100A8      0 0.5772416 0.659 0.196         0    S100A8 (0.652,0.738]
## NBL1        0 0.4801919 0.627 0.159         0      NBL1 (0.566,0.652]
## CCDC3       0 0.4355107 0.601 0.182         0     CCDC3 (0.566,0.652]
## SPARC       0 0.4180143 0.607 0.180         0     SPARC (0.566,0.652]
##             pct.2_cut
## MOXD1    (0.112,0.21]
## ALDH3A1 (0.013,0.112]
## GPX2    (0.013,0.112]
## AHNAK2   (0.112,0.21]
## CDH13    (0.112,0.21]
## LAMB3    (0.112,0.21]
## S100A8   (0.112,0.21]
## NBL1     (0.112,0.21]
## CCDC3    (0.112,0.21]
## SPARC    (0.112,0.21]

We visualize expression levels of those genes on the projection :

plot_list = lapply(rownames(mark_ORS), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Save

We save the list of results :

saveRDS(list_results, file = paste0(out_dir, "/ibl_ors_markers.rds"))

We also save as XLSX file :

list_results2 = list(IBL_vs_all = list_results$IBL_vs_all$mark,
                     IBL_vs_all_selection = list_results$IBL_vs_all$choosen_ones,
                     ORS_vs_all = list_results$ORS_vs_all$mark,
                     ORS_vs_all_selection = list_results$ORS_vs_all$choosen_ones)

openxlsx::write.xlsx(list_results2, file = paste0(out_dir, "/ibl_vs_ors_markers.xlsx"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [4] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] markdown_1.1                DEoptimR_1.0-9             
##  [37] tidygraph_1.1.2             Rcpp_1.0.9                 
##  [39] readr_2.0.2                 KernSmooth_2.23-17         
##  [41] carrier_0.1.0               promises_1.1.0             
##  [43] gdata_2.18.0                DelayedArray_0.12.3        
##  [45] limma_3.42.2                graph_1.64.0               
##  [47] RcppParallel_5.1.4          Hmisc_4.4-0                
##  [49] fs_1.5.2                    RSpectra_0.16-0            
##  [51] fastmatch_1.1-0             ranger_0.12.1              
##  [53] digest_0.6.25               png_0.1-7                  
##  [55] sctransform_0.2.1           cowplot_1.0.0              
##  [57] DOSE_3.12.0                 here_1.0.1                 
##  [59] TInGa_0.0.0.9000            ggraph_2.0.3               
##  [61] pkgconfig_2.0.3             GO.db_3.10.0               
##  [63] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [65] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [67] DropletUtils_1.6.1          reticulate_1.26            
##  [69] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [71] circlize_0.4.15             beeswarm_0.4.0             
##  [73] GetoptLong_1.0.5            xfun_0.35                  
##  [75] bslib_0.3.1                 zoo_1.8-10                 
##  [77] tidyselect_1.1.0            reshape2_1.4.4             
##  [79] purrr_0.3.4                 ica_1.0-2                  
##  [81] pcaPP_1.9-73                viridisLite_0.3.0          
##  [83] rtracklayer_1.46.0          rlang_1.0.2                
##  [85] hexbin_1.28.1               jquerylib_0.1.4            
##  [87] dyneval_0.9.9               glue_1.4.2                 
##  [89] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [91] stringr_1.4.0               lava_1.6.7                 
##  [93] europepmc_0.3               DESeq2_1.26.0              
##  [95] recipes_0.1.17              labeling_0.3               
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            ggwordcloud_0.5.0          
## [147] Biobase_2.46.0              GenomeInfoDb_1.22.1        
## [149] vipor_0.4.5                 lmtest_0.9-38              
## [151] msigdbr_7.5.1               htmlTable_1.13.3           
## [153] triebeard_0.3.0             lsei_1.2-0                 
## [155] xtable_1.8-4                ROCR_1.0-7                 
## [157] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [159] abind_1.4-5                 farver_2.0.3               
## [161] parallelly_1.28.1           RANN_2.6.1                 
## [163] askpass_1.1                 GenomicRanges_1.38.0       
## [165] RcppAnnoy_0.0.16            tibble_3.1.5               
## [167] ggdendro_0.1-20             cluster_2.1.0              
## [169] future.apply_1.5.0          Seurat_3.1.5               
## [171] dendextend_1.15.1           Matrix_1.3-2               
## [173] ellipsis_0.3.2              prettyunits_1.1.1          
## [175] lubridate_1.7.9             ggridges_0.5.2             
## [177] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [179] fgsea_1.12.0                remotes_2.4.2              
## [181] scBFA_1.0.0                 destiny_3.0.1              
## [183] VIM_6.1.1                   testthat_3.1.0             
## [185] htmltools_0.5.2             BiocFileCache_1.10.2       
## [187] yaml_2.2.1                  utf8_1.1.4                 
## [189] plotly_4.9.2.1              XML_3.99-0.3               
## [191] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [193] foreign_0.8-76              withr_2.5.0                
## [195] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [197] xgboost_1.4.1.1             bit64_4.0.5                
## [199] foreach_1.5.0               robustbase_0.93-9          
## [201] Biostrings_2.54.0           GOSemSim_2.13.1            
## [203] rsvd_1.0.3                  memoise_2.0.0              
## [205] evaluate_0.18               forcats_0.5.0              
## [207] rio_0.5.16                  geneplotter_1.64.0         
## [209] tzdb_0.1.2                  caret_6.0-86               
## [211] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [213] curl_4.3                    fdrtool_1.2.15             
## [215] fansi_0.4.1                 highr_0.8                  
## [217] urltools_1.7.3              xts_0.12.1                 
## [219] GSEABase_1.48.0             acepack_1.4.1              
## [221] edgeR_3.28.1                checkmate_2.0.0            
## [223] scds_1.2.0                  cachem_1.0.6               
## [225] npsurv_0.4-0                babelgene_22.3             
## [227] rjson_0.2.20                openxlsx_4.1.5             
## [229] ggrepel_0.9.1               clue_0.3-60                
## [231] rprojroot_2.0.2             stabledist_0.7-1           
## [233] tools_3.6.3                 sass_0.4.0                 
## [235] nichenetr_1.1.1             magrittr_2.0.1             
## [237] RCurl_1.98-1.2              proxy_0.4-24               
## [239] car_3.0-11                  ape_5.3                    
## [241] ggplotify_0.0.5             xml2_1.3.2                 
## [243] httr_1.4.2                  assertthat_0.2.1           
## [245] rmarkdown_2.18              boot_1.3-25                
## [247] globals_0.14.0              R6_2.4.1                   
## [249] Rhdf5lib_1.8.0              nnet_7.3-14                
## [251] RcppHNSW_0.2.0              progress_1.2.2             
## [253] genefilter_1.68.0           statmod_1.4.34             
## [255] gtools_3.8.2                shape_1.4.6                
## [257] HDF5Array_1.14.4            BiocSingular_1.2.2         
## [259] rhdf5_2.30.1                splines_3.6.3              
## [261] AUCell_1.8.0                carData_3.0-4              
## [263] colorspace_1.4-1            generics_0.1.0             
## [265] stats4_3.6.3                base64enc_0.1-3            
## [267] dynfeature_1.0.0            smoother_1.1               
## [269] gridtext_0.1.1              pillar_1.6.3               
## [271] tweenr_1.0.1                sp_1.4-1                   
## [273] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [275] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [277] gtable_0.3.0                zip_2.2.0                  
## [279] knitr_1.41                  latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJJQkwgYW5kIE9SUyBzaWduYXR1cmUiCmF1dGhvcjogIkF1ZHJleSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQnKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQotLS0KCjxzdHlsZT4KYm9keSB7CnRleHQtYWxpZ246IGp1c3RpZnl9Cjwvc3R5bGU+Cgo8IS0tIEF1dG9tYXRpY2FsbHkgY29tcHV0ZXMgYW5kIHByaW50cyBpbiB0aGUgb3V0cHV0IHRoZSBydW5uaW5nIHRpbWUgZm9yIGFueSBjb2RlIGNodW5rIC0tPgpgYGB7ciwgZWNobz1GQUxTRX0KIyBodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9ybWFya2Rvd24vaXNzdWVzLzE0NTMKaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGRfIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PiIsICJzaG93IiwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpLAogIHRpbWVfaXQgPSBsb2NhbCh7CiAgICBub3cgPSBOVUxMCiAgICBmdW5jdGlvbihiZWZvcmUsIG9wdGlvbnMpIHsKICAgICAgaWYgKG9wdGlvbnMkdGltZV9pdCkgewogICAgICAgIGlmIChiZWZvcmUpIHsKICAgICAgICAgIG5vdyA8PSBTeXMudGltZSgpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHJlcyA9IGRpZmZ0aW1lKFN5cy50aW1lKCksIG5vdywgdW5pdHMgPSAic2VjcyIpCiAgICAgICAgICBwYXN0ZSgiKFRpbWUgdG8gcnVuIDoiLCByb3VuZChyZXMsIGRpZ2l0cyA9IDIpLCAicykiKQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0pCikKYGBgCgo8IS0tIFNldCBkZWZhdWx0IHBhcmFtZXRlcnMgZm9yIGFsbCBjaHVua3MgLS0+CmBgYHtyLCBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpzZXQuc2VlZCgxMzM3TCkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAjIGRpc3BsYXkgY29kZQogICAgICAgICAgICAgICAgICAgICAgIyBkaXNwbGF5IGNodW5rIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZm9sZF9vdXRwdXQgPSBGQUxTRSwgIyB1c2VmdWxsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSkKYGBgCgoKVGhpcyBmaWxlIGlzIHVzZWQgdG8gaWRlbnRpZnkgc3BlY2lmaWMgbWFya2VycyBmb3IgSUJMIGFuZCBPUlMuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQoKLmxpYlBhdGhzKCkKYGBgCgojIFByZXBhcmF0aW9uCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHNldCB0aGUgZ2xvYmFsIHNldHRpbmdzIG9mIHRoZSBhbmFseXNpcy4gV2Ugd2lsbCBzdG9yZSBkYXRhIHRoZXJlIDoKCmBgYHtyIG91dF9kaXJ9Cm91dF9kaXIgPSAiLiIKYGBgCgpXZSBsb2FkIHRoZSBkYXRhc2V0IDoKCmBgYHtyIGxvYWRfc29ian0Kc29iaiA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvaHNfaGRfc29iai5yZHMiKSkKc29iagpgYGAKCldlIGxvYWQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiA6CgpgYGB7ciBjdXN0b21fcGFsZXR0ZV9zYW1wbGUsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQpzYW1wbGVfaW5mbyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vMV9tZXRhZGF0YS9oc19oZF9zYW1wbGVfaW5mby5yZHMiKSkKcHJvamVjdF9uYW1lc19vaSA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZQoKZ3JhcGhpY3M6OnBpZShyZXAoMSwgbnJvdyhzYW1wbGVfaW5mbykpLAogICAgICAgICAgICAgIGNvbCA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgIGxhYmVscyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkKYGBgCgpIZXJlIGFyZSBjdXN0b20gY29sb3JzIGZvciBlYWNoIGNlbGwgdHlwZSA6CgpgYGB7ciBjb2xvcl9tYXJrZXJzLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8xX21ldGFkYXRhL2hzX2hkX2NvbG9yX21hcmtlcnMucmRzIikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgIGNvbG9yID0gdW5saXN0KGNvbG9yX21hcmtlcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSkKYGBgCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG9mIGludGVyZXN0IDoKCmBgYHtyIG5hbWUyRH0KbmFtZTJEID0gImhhcm1vbnlfMzhfdHNuZSIKYGBgCgpXZSBkZXNpZ24gYSBjdXN0b20gZnVuY3Rpb24gdG8gbWFrZSBhIGhpc3RvZ3JhbSBhbmQgYSB3b3JkY2xvdWQgdG8gdmlzdWFsaXplIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyA6CgpgYGB7ciBoaXN0X3djX2Z1biwgY2xhc3Muc291Y2UgPSAiZm9sZC1oaWRlIn0KaGlzdF93Y19mdW4gPSBmdW5jdGlvbihtYXJrLCBjb2wpIHsKICBjdXRfY29sb3JzID0gYygiZmlyZWJyaWNrNCIsICJmaXJlYnJpY2syIiwgImluZGlhbnJlZDEiLCAiZGFya3NhbG1vbiIsCiAgICAgICAgICAgICAgICAgImxpZ2h0cGluayIsICJncmF5NTAiLCAia2hha2kzIiwgImRhcmtvbGl2ZWdyZWVuMSIsCiAgICAgICAgICAgICAgICAgIm9saXZlZHJhYjEiLCAiY2hhcnRyZXVzZTIiLCAiY2hhcnRyZXVzZTQiKQogIGN1dF9jb2xvcnNfY29udCA9IGMocmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJlZHMiLCBuID0gOSlbYyg1OjkpXSksCiAgICAgICAgICAgICAgICAgICAgICBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJHcmVlbnMiLCBuID0gOSlbYyg1OjkpXSkKICAKICBpZiAoY29sID09ICJwY3QuMSIpIHsKICAgIG1hcmskcGN0ID0gbWFyayRwY3QuMQogICAgbWFyayRwY3RfY3V0ID0gbWFyayRwY3QuMV9jdXQKICB9IGVsc2UgaWYgKGNvbCA9PSAicGN0LjIiKSB7CiAgICBtYXJrJHBjdCA9IG1hcmskcGN0LjIKICAgIG1hcmskcGN0X2N1dCA9IG1hcmskcGN0LjJfY3V0CiAgICBjdXRfY29sb3JzID0gcmV2KGN1dF9jb2xvcnMpCiAgICBjdXRfY29sb3JzX2NvbnQgPSByZXYoY3V0X2NvbG9yc19jb250KQogIH0gZWxzZSB7CiAgICBzdG9wKCJjb2wgbXVzdCBiZSBlaXRoZXIgcGN0LjEgb3IgcGN0LjIiKQogIH0KICAKICBwX2hpc3QgPSBnZ3Bsb3QyOjpnZ3Bsb3QobWFyaywgbWFwcGluZyA9IGFlcyh4ID0gYXZnX2xvZ0ZDLCBmaWxsID0gcGN0X2N1dCkpICsKICAgIGdncGxvdDI6Omdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wNSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwoYnJlYWtzID0gbGV2ZWxzKG1hcmskcGN0X2N1dCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjdXRfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9IHBhc3RlMChjb2wsICJfY3V0IikpICsKICAgIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKQogIAogIHBfd2MgPSBnZ3Bsb3QyOjpnZ3Bsb3QobWFyaywgYWVzKGxhYmVsID0gZ2VuZV9uYW1lLCBzaXplID0gYXZnX2xvZ0ZDLCBjb2xvciA9IHBjdCkpICsKICAgIGdnd29yZGNsb3VkOjpnZW9tX3RleHRfd29yZGNsb3VkX2FyZWEoc2hvdy5sZWdlbmQgPSBUUlVFLCBzZWVkID0gMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGN1dF9jb2xvcnNfY29udCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gY29sKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA1KSArCiAgICBnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkgKwogICAgZ2dwbG90Mjo6Z3VpZGVzKHNpemUgPSAibm9uZSIpCiAgCiAgcCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwX2hpc3QsIHBfd2MsIG5yb3cgPSAxKQogIAogIHJldHVybihwKQp9CmBgYAoKCiMgVmlzdWFsaXphdGlvbgoKIyMgR2VuZSBleHByZXNzaW9uCgpXZSB2aXN1YWxpemUgZ2VuZSBleHByZXNzaW9uIGZvciBzb21lIG1hcmtlcnMgOgoKYGBge3IgcGxvdF9saXN0X2ZlYXR1cmVzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmZlYXR1cmVzID0gYygicGVyY2VudC5tdCIsICJwZXJjZW50LnJiIiwgIm5GZWF0dXJlX1JOQSIpCgpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXMsIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKQ2x1c3RlciB0eXBlIENsdXN0ZXJzIGFuZCBjZWxsIHR5cGUKCldlIHZpc3VhbGl6ZSBjbHVzdGVycyBhbmQgY2VsbCB0eXBlIDoKCmBgYHtyIHNlZV9jbHVzdGVyaW5nLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmNsdXN0ZXJfcGxvdCA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELCBsYWJlbCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNsdXN0ZXIgSUQiKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCmNlbGxfdHlwZV9wbG90ID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwgbGFiZWwgPSBGQUxTRSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2VsbCB0eXBlIikgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpjZWxsX3R5cGVfcGxvdCB8IGNsdXN0ZXJfcGxvdApgYGAKCldlIHN1bW1hcml6ZSBtYWpvciBjZWxsIHR5cGUgYnkgY2x1c3RlciA6CgpgYGB7ciBjZWxsX3R5cGVfY2x1c3RlcnN9CmNlbGxfdHlwZV9jbHVzdGVycyA9IHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF90eXBlIiwgInNldXJhdF9jbHVzdGVycyIpXSAlPiUKICB0YWJsZSgpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjZWxsX3R5cGVfY2x1c3RlcnMgPSBzZXROYW1lcyhsZXZlbHMoc29iaiRjZWxsX3R5cGUpW2NlbGxfdHlwZV9jbHVzdGVyc10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gbmFtZXMoY2VsbF90eXBlX2NsdXN0ZXJzKSkKCmBgYAoKV2UgZGVmaW5lIGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciB0YWJsZV9jbHVzdGVyX3R5cGV9CnNvYmokY2x1c3Rlcl90eXBlID0gY2VsbF90eXBlX2NsdXN0ZXJzW3NvYmokc2V1cmF0X2NsdXN0ZXJzXSAlPiUKICBhcy5mYWN0b3IoKQp0YWJsZShzb2JqJGNsdXN0ZXJfdHlwZSwgc29iaiRjZWxsX3R5cGUpCmBgYAoKV2UgY29tcGFyZSBjbHVzdGVyIGFubm90YXRpb24gYW5kIGNlbGwgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIHNlZV9jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KY2VsbF90eXBlX3Bsb3QKCnAyID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNsdXN0ZXIgdHlwZSIpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKGNlbGxfdHlwZV9wbG90LCBwMiwgZ3VpZGVzID0gImNvbGxlY3QiKQpgYGAKClRoZXJlIGlzIG1pcy1hbm5vdGF0aW9uIGluIE9SUyAoc21hbGwgcGFydCBjbG9zZSB0byBJQkwpIGFuZCBpbiBJQkwgKHNtYWxsIHBhcnQgaW4gSUZFKSwgc28gd2Uga2VlcCB0aGUgc2luZ2xlLWNlbGwgbGV2ZWwgY2VsbCB0eXBlIGFubm90YXRpb24uCgojIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHBlcmZvcm0gREUgYmV0d2VlbiBpbm5lciBidWxnZSBsYXllciAoSUJMKSBvciBvdXRlciByb290IHNoZWF0aCAoT1JTKSwgYW5kIGFsbCByZW1haW5pbmcgY2VsbHMuIFdlIHNhdmUgdGhlIHJlc3VsdHMgaW4gYSBsaXN0IDoKCmBgYHtyIGxpc3RfcmVzdWx0c30KbGlzdF9yZXN1bHRzID0gbGlzdCgpCmBgYAoKV2UgY2hhbmdlIGNlbGwgaWRlbnRpdGllcyB0byBjZWxsIHR5cGUgOgoKYGBge3IgaWRlbnRfY2VsbF90eXBlfQpTZXVyYXQ6OklkZW50cyhzb2JqKSA9IHNvYmokY2VsbF90eXBlCgp0YWJsZShTZXVyYXQ6OklkZW50cyhzb2JqKSkKYGBgCgojIyBJQkwgbWFya2VycwoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBjb21wYXJlZCBJQkwgdG8gb3RoZXIgY2VsbHMuCgpgYGB7ciBzZWVfSUJMLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KZ3JvdXBfbmFtZSA9ICJJQkxfdnNfYWxsIgoKYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSAiSUJMIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSBncm91cF9uYW1lKQpgYGAKCiMjIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKV2UgaWRlbnRpZnkgc3BlY2lmaWMgbWFya2VycyBmb3IgZWFjaCBwb3B1bGF0aW9uIDoKCmBgYHtyIGRlX0lCTH0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9ICJJQkwiKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6YXJyYW5nZSgtYXZnX2xvZ0ZDLCBwY3QuMSAtIHBjdC4yKQptYXJrJGdlbmVfbmFtZSA9IHJvd25hbWVzKG1hcmspCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRtYXJrID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCkhvdyBtYW55IGdlbmVzIGVucmljaGVkIGluIElCTCA/CgpgYGB7ciBtYXJrX0lCTH0KbWFya19JQkwgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkKCm5yb3cobWFya19JQkwpCmBgYAoKV2UgY3V0IHBjdC4xIGFuZCBwY3QuMiBieSBiaW5zIG9mIDAuMSA6CgpgYGB7ciBtYXJrX2N1dF9JQkx9Cm1hcmtfSUJMJHBjdC4xX2N1dCA9IGN1dChtYXJrX0lCTCRwY3QuMSwgYnJlYWtzID0gMTApCm1hcmtfSUJMJHBjdC4yX2N1dCA9IGN1dChtYXJrX0lCTCRwY3QuMiwgYnJlYWtzID0gMTApCgpoZWFkKG1hcmtfSUJMKQpgYGAKCgojIyMgVmlzdWFsaXphdGlvbgoKV2UgbWFrZSBhIGhpc3RvZ3JhbSBmb3IgYHBjdC4xYCwgYHBjdC4yYCBhbmQgYGF2Z19sb2dGQ2AuCgpgYGB7ciBoaXN0X2ZjX0lCTF8xLCBmaWcud2lkdGggPSAxMywgZmlnLmhlaWdodCA9IDV9Cmhpc3Rfd2NfZnVuKG1hcmtfSUJMLCAicGN0LjEiKQpgYGAKCmBgYHtyIGhpc3RfZmNfSUJMXzIsIGZpZy53aWR0aCA9IDEzLCBmaWcuaGVpZ2h0ID0gNX0KaGlzdF93Y19mdW4obWFya19JQkwsICJwY3QuMiIpCmBgYAoKIyMjIFNlbGVjdGlvbgoKVGhlIGJlc3QgbWFya2VycyBoYXZlIGhpZ2ggcGN0LjEgYW5kIGxvdyBwY3QuMiA6CgpgYGB7ciBtYXJrX0lCTF9zZWxlY3R9Cm1hcmtfSUJMID0gbWFya19JQkwgJT4lCiAgZHBseXI6OmZpbHRlcihwY3QuMSA+IDAuNiAmIHBjdC4yIDwgMC4zKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kY2hvb3Nlbl9vbmVzID0gbWFya19JQkwKCm1hcmtfSUJMCmBgYAoKV2UgdmlzdWFsaXplIGV4cHJlc3Npb24gbGV2ZWxzIG9mIHRob3NlIGdlbmVzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIG1hcmtfSUJMX3NlbGVjdF9zZWUsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KcGxvdF9saXN0ID0gbGFwcGx5KHJvd25hbWVzKG1hcmtfSUJMKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpCmBgYAoKIyMgT1JTIG1hcmtlcnMKCkluIHRoaXMgc2VjdGlvbiwgd2UgY29tcGFyZWQgT1JTIHRvIG90aGVyIGNlbGxzLgoKYGBge3Igc2VlX09SUywgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9Cmdyb3VwX25hbWUgPSAiT1JTX3ZzX2FsbCIKCmFxdWFyaXVzOjpwbG90X3JlZF9hbmRfYmx1ZShzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxID0gIk9SUyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gZ3JvdXBfbmFtZSkKYGBgCgojIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCldlIGlkZW50aWZ5IHNwZWNpZmljIG1hcmtlcnMgZm9yIGVhY2ggcG9wdWxhdGlvbiA6CgpgYGB7ciBkZV9PUlN9Cm1hcmsgPSBTZXVyYXQ6OkZpbmRNYXJrZXJzKHNvYmosIGlkZW50LjEgPSAiT1JTIikKCm1hcmsgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLWF2Z19sb2dGQywgcGN0LjEgLSBwY3QuMikKbWFyayRnZW5lX25hbWUgPSByb3duYW1lcyhtYXJrKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kbWFyayA9IG1hcmsKCmRpbShtYXJrKQpoZWFkKG1hcmssIG4gPSAyMCkKYGBgCgpIb3cgbWFueSBnZW5lcyBlbnJpY2hlZCBpbiBPUlMgPwoKYGBge3IgbWFya19PUlN9Cm1hcmtfT1JTID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApCgpucm93KG1hcmtfT1JTKQpgYGAKCldlIGN1dCBwY3QuMSBhbmQgcGN0LjIgYnkgYmlucyBvZiAwLjEgOgoKYGBge3IgbWFya19jdXRfT1JTfQptYXJrX09SUyRwY3QuMV9jdXQgPSBjdXQobWFya19PUlMkcGN0LjEsIGJyZWFrcyA9IDEwKQptYXJrX09SUyRwY3QuMl9jdXQgPSBjdXQobWFya19PUlMkcGN0LjIsIGJyZWFrcyA9IDEwKQoKaGVhZChtYXJrX09SUykKYGBgCgoKIyMjIFZpc3VhbGl6YXRpb24KCldlIG1ha2UgYSBoaXN0b2dyYW0gZm9yIGBwY3QuMWAsIGBwY3QuMmAgYW5kIGBhdmdfbG9nRkNgLgoKYGBge3IgaGlzdF9mY19PUlNfMSwgZmlnLndpZHRoID0gMTMsIGZpZy5oZWlnaHQgPSA1fQpoaXN0X3djX2Z1bihtYXJrX09SUywgInBjdC4xIikKYGBgCgpgYGB7ciBoaXN0X2ZjX09SU18yLCBmaWcud2lkdGggPSAxMywgZmlnLmhlaWdodCA9IDV9Cmhpc3Rfd2NfZnVuKG1hcmtfT1JTLCAicGN0LjIiKQpgYGAKCiMjIyBTZWxlY3Rpb24KClRoZSBiZXN0IG1hcmtlcnMgaGF2ZSBoaWdoIHBjdC4xIGFuZCBsb3cgcGN0LjIgOgoKYGBge3IgbWFya19PUlNfc2VsZWN0fQptYXJrX09SUyA9IG1hcmtfT1JTICU+JQogIGRwbHlyOjpmaWx0ZXIocGN0LjEgPiAwLjYgJiBwY3QuMiA8IDAuMikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGNob29zZW5fb25lcyA9IG1hcmtfT1JTCgptYXJrX09SUwpgYGAKCldlIHZpc3VhbGl6ZSBleHByZXNzaW9uIGxldmVscyBvZiB0aG9zZSBnZW5lcyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBtYXJrX09SU19zZWxlY3Rfc2VlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpwbG90X2xpc3QgPSBsYXBwbHkocm93bmFtZXMobWFya19PUlMpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMykKYGBgCgoKIyBTYXZlCgpXZSBzYXZlIHRoZSBsaXN0IG9mIHJlc3VsdHMgOgoKYGBge3Igc2F2ZV9saXN0X3Jlc3VsdHN9CnNhdmVSRFMobGlzdF9yZXN1bHRzLCBmaWxlID0gcGFzdGUwKG91dF9kaXIsICIvaWJsX29yc19tYXJrZXJzLnJkcyIpKQpgYGAKCldlIGFsc28gc2F2ZSBhcyBYTFNYIGZpbGUgOgoKYGBge3Igc2F2ZV9saXN0X3Jlc3VsdHMyfQpsaXN0X3Jlc3VsdHMyID0gbGlzdChJQkxfdnNfYWxsID0gbGlzdF9yZXN1bHRzJElCTF92c19hbGwkbWFyaywKICAgICAgICAgICAgICAgICAgICAgSUJMX3ZzX2FsbF9zZWxlY3Rpb24gPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX2FsbCRjaG9vc2VuX29uZXMsCiAgICAgICAgICAgICAgICAgICAgIE9SU192c19hbGwgPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX2FsbCRtYXJrLAogICAgICAgICAgICAgICAgICAgICBPUlNfdnNfYWxsX3NlbGVjdGlvbiA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfYWxsJGNob29zZW5fb25lcykKCm9wZW54bHN4Ojp3cml0ZS54bHN4KGxpc3RfcmVzdWx0czIsIGZpbGUgPSBwYXN0ZTAob3V0X2RpciwgIi9pYmxfdnNfb3JzX21hcmtlcnMueGxzeCIpKQpgYGAKCgojIFIgU2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmluZm8sIGVjaG8gPSBGQUxTRSwgZm9sZF9vdXRwdXQgPSBUUlVFfQpzZXNzaW9uSW5mbygpCmBgYAoK